name: bun-windows

concurrency:
  group: bun-windows-${{ github.ref }}
  cancel-in-progress: true

env:
  # note: in other files, this version is only the major version, but for windows it is the full version
  LLVM_VERSION: 16.0.6
  BUN_DOWNLOAD_URL_BASE: https://pub-5e11e972747a44bf9aaf9394f185a982.r2.dev/releases/latest

  tag: bun-windows
  # TODO: wire this up to workflow_dispatch.
  # github's expression syntax makes this hard to set a default to true
  canary: true

on:
  push:
    branches: [main]
    paths:
      - "src/**/*"
      - "test/**/*"
      - "packages/bun-usockets/src/**/*"
      - "packages/bun-uws/src/**/*"
      - "CMakeLists.txt"
      - "build.zig"
      - "Makefile"
      - "Dockerfile"
  pull_request:
    branches: [main]
    paths:
      - "src/**/*"
      - "test/**/*"
      - "packages/bun-usockets/src/**/*"
      - "packages/bun-uws/src/**/*"
      - "CMakeLists.txt"
      - "build.zig"
      - "Makefile"
      - "Dockerfile"
  # Allows you to run this workflow manually from the Actions tab
  workflow_dispatch:
    # inputs:
    #   is-canary:
    #     type: boolean
    #     description: Is Canary Build?
    #     default: true

jobs:
  windows-zig:
    strategy:
      fail-fast: false
      matrix:
        cpu: [haswell, nehalem]
        arch: [x86_64]
    name: Zig Build
    runs-on: med-ubuntu
    timeout-minutes: 60
    if: github.repository_owner == 'oven-sh'
    steps:
      - run: git config --global core.autocrlf false && git config --global core.eol lf
      - uses: actions/checkout@v4
      - name: Setup Docker Buildx
        uses: docker/setup-buildx-action@v3
        id: buildx
        with:
          install: true

      - name: Login to GitHub Container Registry
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Calculate Canary Revision
        if: ${{ env.canary == 'true' }}
        id: canary
        run: |
          echo "canary_revision=$(GITHUB_TOKEN="${{ secrets.GITHUB_TOKEN }}" bash ./scripts/calculate-canary-revision.sh --raw)" >> $GITHUB_OUTPUT

      - name: Compile Zig Object
        uses: docker/build-push-action@v5
        if: runner.arch == 'X64'
        with:
          context: .
          push: false
          # This doesnt seem to work
          # cache-from: type=s3,endpoint_url=${{ secrets.CACHE_S3_ENDPOINT }},blobs_prefix=docker_blobs/,manifests_prefix=docker_manifests/,access_key_id=${{ secrets.CACHE_S3_ACCESS_KEY_ID }},secret_access_key=${{ secrets.CACHE_S3_SECRET_ACCESS_KEY }},bucket=bun,region=auto
          # cache-to: type=s3,endpoint_url=${{ secrets.CACHE_S3_ENDPOINT }},blobs_prefix=docker_blobs/,manifests_prefix=docker_manifests/,access_key_id=${{ secrets.CACHE_S3_ACCESS_KEY_ID }},secret_access_key=${{ secrets.CACHE_S3_SECRET_ACCESS_KEY }},bucket=bun,region=auto
          build-args: |
            BUILDARCH=${{ runner.arch == 'X64' && 'amd64' || 'arm64' }}
            BUILD_MACHINE_ARCH=${{ runner.arch == 'X64' && 'x86_64' || 'aarch64' }}
            ARCH=${{ matrix.arch }}
            CPU_TARGET=${{ matrix.cpu }}
            TRIPLET=${{ matrix.arch }}-windows-msvc
            GIT_SHA=${{ github.sha }}
            CANARY=${{ env.canary == 'true' && steps.canary.outputs.canary_revision || '0' }}
            ZIG_OPTIMIZE=ReleaseSafe
          # TODO(@paperdave): enable ASSERTIONS=1
          platforms: linux/${{ runner.arch == 'X64' && 'amd64' || 'arm64' }}
          target: build_release_obj
          outputs: type=local,dest=${{runner.temp}}/release

      - name: Upload Zig Object
        uses: actions/upload-artifact@v4
        with:
          name: ${{ env.tag }}-${{ matrix.arch == 'x86_64' && 'x64' || 'aarch64' }}-zig${{ matrix.cpu == 'nehalem' && '-baseline' || '' }}
          path: ${{runner.temp}}/release/bun-zig.o

  windows-dependencies:
    name: Dependencies
    runs-on: windows
    timeout-minutes: 60
    strategy:
      fail-fast: false
      matrix:
        cpu: [haswell, nehalem]
        arch: [x86_64]
    steps:
      - run: git config --global core.autocrlf false && git config --global core.eol lf
      - name: Checkout
        uses: actions/checkout@v4
      - name: Clone Submodules
        run: .\scripts\update-submodules.ps1
      - name: Hash submodule versions
        shell: pwsh
        run: |
          $data = "$(& {
            git submodule | Where-Object { $_ -notmatch 'WebKit' }
            clang --version
            rustc --version
            Get-Content -Path (Get-ChildItem -Path 'scripts/build*.ps1', 'scripts/all-dependencies.ps1', 'scripts/env.ps1' | Sort-Object -Property Name).FullName | Out-String
            echo 1
          })"
          $hash = ( -join ((New-Object -TypeName System.Security.Cryptography.SHA1CryptoServiceProvider).ComputeHash([System.Text.Encoding]::UTF8.GetBytes($data)) | ForEach-Object { $_.ToString("x2") } )).Substring(0, 10)
          echo "sha=${hash}" >> $env:GITHUB_OUTPUT
        id: submodule-versions

      - name: Try fetch dependencies
        id: cache-deps-restore
        uses: actions/cache/restore@v4
        with:
          path: bun-deps
          key: bun-deps-${{ env.tag }}-${{ matrix.arch == 'x86_64' && 'x64' || 'aarch64' }}${{ matrix.cpu == 'nehalem' && '-baseline' || '' }}-${{ steps.submodule-versions.outputs.sha }}

      - name: Install LLVM ${{ env.LLVM_VERSION }}
        if: ${{ !steps.cache-deps-restore.outputs.cache-hit }}
        uses: KyleMayes/install-llvm-action@1a3da29f56261a1e1f937ec88f0856a9b8321d7e
        with:
          version: ${{ env.LLVM_VERSION }}

      - name: Install Ninja
        if: ${{ !steps.cache-deps-restore.outputs.cache-hit }}
        run: choco install -y ninja

      - name: Build Dependencies
        if: ${{ !steps.cache-deps-restore.outputs.cache-hit }}
        run: |
          .\scripts\env.ps1 ${{ matrix.cpu == 'nehalem' && '-Baseline' || '' }}
          Invoke-WebRequest -Uri "https://www.nasm.us/pub/nasm/releasebuilds/2.16.01/win64/nasm-2.16.01-win64.zip" -OutFile nasm.zip
          Expand-Archive nasm.zip (mkdir -Force "nasm")
          $Nasm = (Get-ChildItem "nasm")
          $env:Path += ";${Nasm}"
          $env:BUN_DEPS_OUT_DIR = (mkdir -Force "./bun-deps")
          .\scripts\all-dependencies.ps1

      - name: Upload Dependencies
        uses: actions/upload-artifact@v4
        with:
          name: ${{ env.tag }}-${{ matrix.arch == 'x86_64' && 'x64' || 'aarch64' }}-deps${{ matrix.cpu == 'nehalem' && '-baseline' || '' }}
          path: bun-deps/

      - name: Cache Dependencies
        if: ${{ !steps.cache-deps-restore.outputs.cache-hit }}
        id: cache-deps-save
        uses: actions/cache/save@v4
        with:
          path: bun-deps
          key: ${{ steps.cache-deps-restore.outputs.cache-primary-key }}

  # TODO(@paperdave): stop relying on this and use bun.exe to build itself.
  # we cant do that now because there isn't a tagged release to use.
  #
  # and at the time of writing, the minimum canary required to work is not
  # yet released as it is the one *this* commit.
  windows-codegen:
    name: Codegen
    runs-on: ubuntu-latest
    timeout-minutes: 10
    if: github.repository_owner == 'oven-sh'
    strategy:
      fail-fast: false
      matrix:
        arch: [x86_64]
    steps:
      - uses: actions/checkout@v4
      - run: |
          curl -fsSL $BUN_DOWNLOAD_URL_BASE/bun-linux-x64.zip > bun.zip
          unzip bun.zip
          export PATH="$PWD/bun-linux-x64:$PATH"
          ./scripts/cross-compile-codegen.sh win32 x64
      # Sort of a hack to do this step in the codegen stage
      - name: Calculate Canary Revision
        if: ${{ env.canary == 'true' }}
        run: |
          echo "canary_revision=$(GITHUB_TOKEN="${{ secrets.GITHUB_TOKEN }}" bash ./scripts/calculate-canary-revision.sh --raw)" > build-codegen-win32-x64/.canary_revision
      - uses: actions/upload-artifact@v4
        with:
          name: ${{ env.tag }}-${{ matrix.arch == 'x86_64' && 'x64' || 'aarch64' }}-codegen
          path: build-codegen-win32-x64/

  windows-cpp:
    name: C++ Build
    needs: [windows-codegen]
    runs-on: windows
    if: github.repository_owner == 'oven-sh'
    timeout-minutes: 90
    strategy:
      fail-fast: false
      matrix:
        cpu: [haswell, nehalem]
        arch: [x86_64]
    steps:
      - run: git config --global core.autocrlf false && git config --global core.eol lf
      - uses: actions/checkout@v4
      - uses: KyleMayes/install-llvm-action@1a3da29f56261a1e1f937ec88f0856a9b8321d7e
        with:
          version: ${{ env.LLVM_VERSION }}
      - run: choco install -y ninja
      - name: Download Codegen
        uses: actions/download-artifact@v4
        with:
          name: ${{ env.tag }}-${{ matrix.arch == 'x86_64' && 'x64' || 'aarch64' }}-codegen
          path: build
      - name: Build C++
        run: |
          # Using SCCache was blocked by an issue that is fixed in a newer version.
          # TODO UPDATE
          # $sczip = "sccache-v0.6.0-x86_64-pc-windows-msvc"
                    
          # Invoke-WebRequest -Uri "https://github.com/mozilla/sccache/releases/download/v0.6.0/${sczip}.zip" -OutFile "${sczip}.zip"
          # Expand-Archive "${sczip}.zip"
          # $env:SCCACHE_BUCKET="bun"
          # $env:SCCACHE_REGION="auto"
          # $env:SCCACHE_S3_USE_SSL="true"
          # $env:SCCACHE_ENDPOINT="${{ secrets.CACHE_S3_ENDPOINT }}"
          # $env:AWS_ACCESS_KEY_ID="${{ secrets.CACHE_S3_ACCESS_KEY_ID }}"
          # $env:AWS_SECRET_ACCESS_KEY="${{ secrets.CACHE_S3_SECRET_ACCESS_KEY }}"
          # $SCCACHE="$PWD/${sczip}/${sczip}/sccache.exe"

          $CANARY_REVISION = if (Test-Path build/.canary_revision) { Get-Content build/.canary_revision } else { "0" }

          .\scripts\env.ps1 ${{ matrix.cpu == 'nehalem' && '-Baseline' || '' }}
          .\scripts\update-submodules.ps1
          .\scripts\build-libuv.ps1 -CloneOnly $True
          cd build
          # "-DCCACHE_PROGRAM=${SCCACHE}"
          cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release `
            -DNO_CODEGEN=1 `
            -DNO_CONFIGURE_DEPENDS=1 `
            "-DCANARY=${CANARY_REVISION}" `
            -DBUN_CPP_ONLY=1 ${{ matrix.cpu == 'nehalem' && '-DUSE_BASELINE_BUILD=1' || '' }}
          if ($LASTEXITCODE -ne 0) { throw "CMake configuration failed" }
          .\compile-cpp-only.ps1 -v
          if ($LASTEXITCODE -ne 0) { throw "C++ compilation failed" }
      - uses: actions/upload-artifact@v4
        with:
          name: ${{ env.tag }}-${{ matrix.arch == 'x86_64' && 'x64' || 'aarch64' }}-cpp${{ matrix.cpu == 'nehalem' && '-baseline' || '' }}
          path: build/bun-cpp-objects.a

  windows-link:
    strategy:
      fail-fast: false
      matrix:
        cpu: [haswell, nehalem]
        arch: [x86_64]
    name: Link
    needs: [windows-dependencies, windows-codegen, windows-cpp, windows-zig]
    runs-on: windows-small
    if: github.repository_owner == 'oven-sh'
    timeout-minutes: 30
    permissions: write-all
    steps:
      - run: git config --global core.autocrlf false && git config --global core.eol lf
      - uses: actions/checkout@v4
      - uses: KyleMayes/install-llvm-action@1a3da29f56261a1e1f937ec88f0856a9b8321d7e
        with:
          version: ${{ env.LLVM_VERSION }}
      - run: choco install -y ninja
      - name: Download Codegen
        uses: actions/download-artifact@v4
        with:
          name: ${{ env.tag }}-${{ matrix.arch == 'x86_64' && 'x64' || 'aarch64' }}-codegen
          path: build
      - name: Download Dependencies
        uses: actions/download-artifact@v4
        with:
          name: ${{ env.tag }}-${{ matrix.arch == 'x86_64' && 'x64' || 'aarch64' }}-deps${{ matrix.cpu == 'nehalem' && '-baseline' || '' }}
          path: bun-deps
      - name: Download Zig Object
        uses: actions/download-artifact@v4
        with:
          name: ${{ env.tag }}-${{ matrix.arch == 'x86_64' && 'x64' || 'aarch64' }}-zig${{ matrix.cpu == 'nehalem' && '-baseline' || '' }}
          path: bun-zig
      - name: Download C++ Objects
        uses: actions/download-artifact@v4
        with:
          name: ${{ env.tag }}-${{ matrix.arch == 'x86_64' && 'x64' || 'aarch64' }}-cpp${{ matrix.cpu == 'nehalem' && '-baseline' || '' }}
          path: bun-cpp
      - name: Link
        run: |
          .\scripts\update-submodules.ps1
          .\scripts\env.ps1 ${{ matrix.cpu == 'nehalem' && '-Baseline' || '' }}
          Set-Location build
          $CANARY_REVISION = if (Test-Path build/.canary_revision) { Get-Content build/.canary_revision } else { "0" }
          cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release `
            -DNO_CODEGEN=1 `
            -DNO_CONFIGURE_DEPENDS=1 `
            "-DCANARY=${CANARY_REVISION}" `
            -DBUN_LINK_ONLY=1 `
            "-DBUN_DEPS_OUT_DIR=$(Resolve-Path ../bun-deps)" `
            "-DBUN_CPP_ARCHIVE=$(Resolve-Path ../bun-cpp/bun-cpp-objects.a)" `
            "-DBUN_ZIG_OBJ=$(Resolve-Path ../bun-zig/bun-zig.o)" `
            ${{ matrix.cpu == 'nehalem' && '-DUSE_BASELINE_BUILD=1' || '' }}
          if ($LASTEXITCODE -ne 0) { throw "CMake configuration failed" }
          ninja -v
          if ($LASTEXITCODE -ne 0) { throw "Link failed!" }
      - name: Package
        run: |
          $Dist = mkdir -Force "${{ env.tag }}-${{ matrix.arch == 'x86_64' && 'x64' || 'aarch64' }}${{ matrix.cpu == 'nehalem' && '-baseline' || '' }}"
          cp -r build\bun.exe "$Dist\bun.exe"
          Compress-Archive "$Dist" "${Dist}.zip"
          $Dist = "$Dist-profile"
          MkDir -Force "$Dist"
          cp -r build\bun.exe "$Dist\bun.exe"
          cp -r build\bun.pdb "$Dist\bun.pdb"
          Compress-Archive "$Dist" "$Dist.zip"

      - uses: actions/upload-artifact@v4
        with:
          name: ${{ env.tag }}-${{ matrix.arch == 'x86_64' && 'x64' || 'aarch64' }}${{ matrix.cpu == 'nehalem' && '-baseline' || '' }}
          path: ${{ env.tag }}-${{ matrix.arch == 'x86_64' && 'x64' || 'aarch64' }}${{ matrix.cpu == 'nehalem' && '-baseline' || '' }}.zip
      - uses: actions/upload-artifact@v4
        with:
          name: ${{ env.tag }}-${{ matrix.arch == 'x86_64' && 'x64' || 'aarch64' }}${{ matrix.cpu == 'nehalem' && '-baseline' || '' }}-profile
          path: ${{ env.tag }}-${{ matrix.arch == 'x86_64' && 'x64' || 'aarch64' }}${{ matrix.cpu == 'nehalem' && '-baseline' || '' }}-profile.zip
      - name: Release
        id: release
        uses: ncipollo/release-action@v1
        if: |
          github.repository_owner == 'oven-sh'
          && github.ref == 'refs/heads/main'
        with:
          prerelease: true
          body: "This canary release of Bun corresponds to the commit [${{ github.sha }}]"
          allowUpdates: true
          replacesArtifacts: true
          generateReleaseNotes: true
          artifactErrorsFailBuild: true
          token: ${{ secrets.GITHUB_TOKEN }}
          name: "Canary (${{github.sha}})"
          tag: "canary"
          artifacts: "${{env.tag}}-${{ matrix.arch == 'x86_64' && 'x64' || 'aarch64' }}${{ matrix.cpu == 'nehalem' && '-baseline' || '' }}.zip,${{env.tag}}-${{ matrix.arch == 'x86_64' && 'x64' || 'aarch64' }}${{ matrix.cpu == 'nehalem' && '-baseline' || '' }}-profile.zip"
      - uses: sarisia/actions-status-discord@v1
        if: failure() && github.repository_owner == 'oven-sh' && github.event_name == 'pull_request'
        with:
          title: ""
          webhook: ${{ secrets.DISCORD_WEBHOOK }}
          status: ${{ job.status }}
          noprefix: true
          nocontext: true
          description: |
            ### [${{github.event.pull_request.title}}](https://github.com/oven-sh/bun/pull/${{github.event.number}})

            @${{ github.actor }}

            Build failed on Windows ${{ matrix.arch }}${{ matrix.cpu == 'nehalem' && ' Baseline' || '' }}

            **[Build Output](https://github.com/oven-sh/bun/actions/runs/${{github.run_id}})** | [Commit](https://github.com/oven-sh/bun/commits/${{github.sha}})
  windows-test:
    name: Test
    runs-on: windows-small
    needs: [windows-link]
    if: github.event_name == 'pull_request' && github.repository_owner == 'oven-sh'
    permissions:
      pull-requests: write
    timeout-minutes: 180
    outputs:
      failing_tests: ${{ steps.test.outputs.failing_tests }}
      failing_tests_count: ${{ steps.test.outputs.failing_tests_count }}
    strategy:
      fail-fast: false
      matrix:
        # TODO: test baseline, disabled due to noise
        cpu: [haswell]
        arch: [x86_64]
    steps:
      - run: git config --global core.autocrlf false && git config --global core.eol lf
      - id: checkout
        name: Checkout
        uses: actions/checkout@v4
        with:
          submodules: false
      - id: download
        name: Download Release
        uses: actions/download-artifact@v4
        with:
          name: ${{ env.tag }}-${{ matrix.arch == 'x86_64' && 'x64' || 'aarch64' }}${{ matrix.cpu == 'nehalem' && '-baseline' || '' }}-profile
          path: ${{runner.temp}}/release
      - name: Install Bun
        run: |
          cd ${{runner.temp}}/release
          unzip ${{env.tag}}-${{ matrix.arch == 'x86_64' && 'x64' || 'aarch64' }}${{ matrix.cpu == 'nehalem' && '-baseline' || '' }}-profile.zip
          cd ${{env.tag}}-${{ matrix.arch == 'x86_64' && 'x64' || 'aarch64' }}${{ matrix.cpu == 'nehalem' && '-baseline' || '' }}-profile
          pwd >> $env:GITHUB_PATH
      - name: Install Node
        uses: actions/setup-node@v4
        with:
          node-version: 20
      - name: Install dependencies
        run: |
          # bun install --verbose
          # bun install --cwd=test --verbose
          # bun install --cwd=packages/bun-internal-test --verbose

          npm install
          cd test && npm install
          cd ../packages/bun-internal-test && npm install
          cd ../..
      - id: test
        name: Run tests
        env:
          SMTP_SENDGRID_SENDER: ${{ secrets.SMTP_SENDGRID_SENDER }}
          TMPDIR: ${{runner.temp}}
          TLS_MONGODB_DATABASE_URL: ${{ secrets.TLS_MONGODB_DATABASE_URL }}
          TLS_POSTGRES_DATABASE_URL: ${{ secrets.TLS_POSTGRES_DATABASE_URL }}
        run: |
          try {
            $ErrorActionPreference = "SilentlyContinue"
            $null = node packages/bun-internal-test/src/runner.node.mjs ${{runner.temp}}/release/${{env.tag}}-${{ matrix.arch == 'x86_64' && 'x64' || 'aarch64' }}${{ matrix.cpu == 'nehalem' && '-baseline' || '' }}-profile/bun.exe || $true
          } catch {}
          $ErrorActionPreference = "Stop"
      - uses: sarisia/actions-status-discord@v1
        if: always() && steps.test.outputs.failing_tests != '' && github.event_name == 'pull_request'
        with:
          title: ""
          webhook: ${{ secrets.DISCORD_WEBHOOK_WINTEST }}
          status: "failure"
          noprefix: true
          nocontext: true
          description: |
            ### ❌🪟 [${{github.event.pull_request.title}}](https://github.com/oven-sh/bun/pull/${{github.event.number}})

            @${{ github.actor }}, there are **${{ steps.test.outputs.failing_test_count }} failing tests** on Windows ${{ matrix.arch }}${{ matrix.cpu == 'nehalem' && ' Baseline' || '' }}

            ${{ steps.test.outputs.failing_tests }}

            [Full Test Output](https://github.com/oven-sh/bun/actions/runs/${{github.run_id}})
      - uses: sarisia/actions-status-discord@v1
        if: always() && steps.test.outputs.regressing_tests != '' && github.event_name == 'pull_request'
        with:
          title: ""
          webhook: ${{ secrets.DISCORD_WEBHOOK }}
          status: "failure"
          noprefix: true
          nocontext: true
          description: |
            ### ❌🪟 [${{github.event.pull_request.title}}](https://github.com/oven-sh/bun/pull/${{github.event.number}})

            @${{ github.actor }}, there are **${{ steps.test.outputs.regressing_test_count }} test regressions** on Windows ${{ matrix.arch }}${{ matrix.cpu == 'nehalem' && ' Baseline' || '' }}

            ${{ steps.test.outputs.regressing_tests }}

            [Full Test Output](https://github.com/oven-sh/bun/actions/runs/${{github.run_id}})
      - name: Comment on PR
        if: always() && steps.test.outputs.regressing_tests != '' && github.event_name == 'pull_request'
        uses: thollander/actions-comment-pull-request@v2
        with:
          comment_tag: test-windows-${{ matrix.arch }}-${{ matrix.cpu }}
          message: |
            ### ❌🪟 @${{ github.actor }}, there are **${{ steps.test.outputs.regressing_test_count }} test regressions** on Windows ${{ matrix.arch }}${{ matrix.cpu == 'nehalem' && ' Baseline' || '' }}

            ${{ steps.test.outputs.regressing_tests }}

            [Full Test Output](https://github.com/oven-sh/bun/actions/runs/${{github.run_id}})
      - name: Uncomment on PR
        if: steps.test.outputs.regressing_tests == '' && github.event_name == 'pull_request'
        uses: thollander/actions-comment-pull-request@v2
        with:
          comment_tag: test-windows-${{ matrix.arch }}-${{ matrix.cpu }}
          mode: upsert
          create_if_not_exists: false
          message: |
            ✅🪟 Test regressions on Windows ${{ matrix.arch }}${{ matrix.cpu == 'nehalem' && ' Baseline' || '' }} have been resolved.
      - id: fail
        name: Fail the build
        if: steps.test.outputs.regressing_tests != '' && github.event_name == 'pull_request'
        run: exit 1
